package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

var start time.Time

// 定义一个不可导出的类型，作为valueCtx的key
type ctxKey string

// player 没秒钟报告一次，如果收到取消信号，报告退出后返回
func player(ctx context.Context, name string) {
	for {
		select {
		case <-ctx.Done():
			fmt.Printf("%7.4f %s: 退出。\n", time.Since(start).Seconds(), name)
			return
		default:
			fmt.Printf("%7.4f %s: 进行中...\n", time.Since(start).Seconds(), name)
			time.Sleep(time.Second * 1)
		}
	}
}

func main() {
	start = time.Now()
	var wg sync.WaitGroup
	// 创建一个Room的Context，忽略时间解析错误
	deadline, _ := time.ParseInLocation("2006-01-02 15:04", "2020-12-03 20:48", time.Local)
	ctxRoom, cancelRoom := context.WithDeadline(context.Background(), deadline)
	defer cancelRoom()
	ctxRoom = context.WithValue(ctxRoom, ctxKey("room"), "SH")
	wg.Add(1)
	go func() { // 接收Room关闭的信号，打印消息
		defer wg.Done()
		<-ctxRoom.Done()
		fmt.Printf("%7.4f 关闭 Room %s\n", time.Since(start).Seconds(), ctxRoom.Value(ctxKey("room")))
	}()

	// 在Room下创建两个Table的Context
	ctxTableA, cancelTableA := context.WithTimeout(ctxRoom, time.Second*3)
	defer cancelTableA()
	ctxTableA = context.WithValue(ctxTableA, ctxKey("table"), "TabelA")
	wg.Add(1)
	go func() {
		defer wg.Done()
		<-ctxTableA.Done()
		fmt.Printf("%7.4f 关闭 %s\n", time.Since(start).Seconds(), ctxTableA.Value(ctxKey("table")))
	}()

	ctxTableB, cancelTableB := context.WithTimeout(ctxRoom, time.Second*300)
	defer cancelTableB()
	ctxTableB = context.WithValue(ctxTableB, ctxKey("table"), "TabelB")
	wg.Add(1)
	go func() {
		defer wg.Done()
		<-ctxTableB.Done()
		fmt.Printf("%7.4f 关闭 %s\n", time.Since(start).Seconds(), ctxTableB.Value(ctxKey("table")))
	}()

	// 创建Player
	wg.Add(1)
	go func(ctx context.Context, name string) {
		fullName := fmt.Sprintf("%s_%s_%s", ctx.Value(ctxKey("room")), ctx.Value(ctxKey("table")), name)
		defer wg.Done()
		player(ctx, fullName)
	}(ctxTableA, "P1")
	wg.Add(1)
	go func(ctx context.Context, name string) {
		fullName := fmt.Sprintf("%s_%s_%s", ctx.Value(ctxKey("room")), ctx.Value(ctxKey("table")), name)
		defer wg.Done()
		player(ctx, fullName)
	}(ctxTableA, "P2")
	wg.Add(1)
	go func(ctx context.Context, name string) {
		fullName := fmt.Sprintf("%s_%s_%s", ctx.Value(ctxKey("room")), ctx.Value(ctxKey("table")), name)
		defer wg.Done()
		player(ctx, fullName)
	}(ctxTableB, "P3")
	wg.Add(1)
	go func(ctx context.Context, name string) {
		fullName := fmt.Sprintf("%s_%s_%s", ctx.Value(ctxKey("room")), ctx.Value(ctxKey("table")), name)
		defer wg.Done()
		player(ctx, fullName)
	}(ctxTableB, "P4")

	// 等待所有goroutine结束
	wg.Wait()
	fmt.Printf("%7.4f 结束。\n", time.Since(start).Seconds())
}
